package com.arpa.oms.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.arpa.core.web.Result;
import com.arpa.ntocc.common.common.cache.PartyCache;
import com.arpa.ntocc.common.common.cache.ShipmentCache;
import com.arpa.ntocc.common.common.exception.ServiceException;
import com.arpa.ntocc.common.common.util.UserUtil;
import com.arpa.wms.common.util.OrderIdGeneratorUtil;
import com.arpa.wms.domain.consts.CommonConst;
import com.arpa.wms.domain.consts.NumConst;
import com.arpa.wms.domain.dto.PackBillingMethodDTO;
import com.arpa.wms.domain.dto.PrepackagedDTO;
import com.arpa.wms.domain.dto.PrepackagedDistributionDTO;
import com.arpa.wms.domain.dto.PrepackagedItemDTO;
import com.arpa.wms.domain.entity.*;
import com.arpa.wms.domain.enums.*;
import com.arpa.wms.domain.vo.InventoryWithDistributionVO;
import com.arpa.wms.domain.vo.PrepackagedItemVO;
import com.arpa.wms.domain.vo.PrepackagedItemWithDistributionVO;
import com.arpa.wms.domain.vo.PrepackagedVO;
import com.arpa.wms.mapper.PrepackagedMapper;
import com.arpa.wms.service.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.arpa.wms.domain.consts.NumConst.NUM_ONE;

/**
 * <p>
 * 预打包单 服务实现类
 * </p>
 *
 * @author leo
 * @since 2021-07-02
 */
@Service
public class PrepackagedServiceImpl extends ServiceImpl<PrepackagedMapper, Prepackaged> implements IPrepackagedService {

    @Autowired
    private ShipmentCache shipmentCache;
    @Autowired
    private PartyCache partyCache;
    @Autowired
    private IPrepackagedItemService prepackagedItemService;
    @Autowired
    private IPrepackagedDistributionService prepackagedDistributionService;
    @Autowired
    @Lazy
    private IInventoryService inventoryService;
    @Autowired
    private IPickingService pickingService;
    @Autowired
    private IPickingItemService pickingItemService;
    @Autowired
    private IPickingRegisterService pickingRegisterService;
    @Autowired
    private IWmsTaskService wmsTaskService;
    @Autowired
    private IInventoryRecordService inventoryRecordService;
    @Autowired
    private IPackTaskService packTaskService;
    @Autowired
    private IPackTaskItemService packTaskItemService;




    /**
     * 保存预打包单
     *
     * @param entity
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Result saveWithGoods(Prepackaged entity) {
        if (ObjectUtil.isNull(entity) || IterUtil.isEmpty(entity.getPrepackagedItemList())) {
            return Result.error("提交的数据为空，请确认");
        }
        if (StrUtil.isEmpty(entity.getShipmentName())) {
            entity.setShipmentName(shipmentCache.translate(entity.getShipmentCode()));
        }
        List<PrepackagedItem> prepackagedItems = entity.getPrepackagedItemList();
        // 校验是否有重复项
        validDuplication(prepackagedItems);

        // 用于判断是新增或修改
        boolean isCreated = false;
        // 预打包单号，来源单号的生成规则
        if (StrUtil.isBlank(entity.getCode())) {
            // 使用PP+日期+流水号
            String codePrefix = OrderIdGeneratorUtil.OrderTypeEnum.PP + DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN);
            entity.setCode(codePrefix + OrderIdGeneratorUtil.getAutoIncrementIndex(codePrefix,3));
            isCreated = true;
        }
        String createdBy = UserUtil.getCode();
        String createdName = partyCache.translate(createdBy);
        //当前操作人
        entity.setCreatedBy(createdBy);
        entity.setCreatedName(createdName);
        entity.setModifiedBy(createdBy);
        entity.setModifiedName(createdName);
        entity.setGroupCode(UserUtil.getBranchCode());

        // 用于计算商品种数
        Map<String, Boolean> goodsSortMap;
        goodsSortMap = new HashMap<>();
        // 明细增加必要项
        prepackagedItems.forEach(info -> {
            info.setCode(IdUtil.simpleUUID());
            info.setPrepackagedCode(entity.getCode());
            info.setCreatedBy(createdBy);
            info.setNotDistributionQuantity(info.getTotalQuantity());
            info.setGroupCode(UserUtil.getBranchCode());
            goodsSortMap.put(info.getGoodsCode(), true);
        });

        entity.setTypeQuantity(goodsSortMap.size());
        BigDecimal totalQuantity = prepackagedItems.stream().map(PrepackagedItem::getTotalQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
        //获取明细单包裹组合数量之和
        BigDecimal totalCombinationQuantity = prepackagedItems.stream().map(PrepackagedItem::getCombinationQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
        entity.setTotalQuantity(totalQuantity);
        entity.setTotalCombinationQuantity(totalCombinationQuantity);

        // 新增/保存
        if (isCreated) {
            super.save(entity);
        } else {
            this.updateByCode(entity);
        }
        prepackagedItemService.remove(new QueryWrapper<PrepackagedItem>().lambda()
                .eq(PrepackagedItem::getPrepackagedCode, entity.getCode())
                .eq(PrepackagedItem::getGroupCode,UserUtil.getBranchCode()));
        prepackagedItemService.saveBatch(prepackagedItems);

        return Result.ok("保存成功");
    }

    private void validDuplication(List<PrepackagedItem> prepackagedItems) {
        // 校验是否有重复项
        Set<String> validDuplication = new HashSet<>();
        prepackagedItems.stream().forEach(e ->{
            String validKey = e.getGoodsCode() + "-" + e.getGoodsUnit();
            if (validDuplication.contains(validKey)) {
                throw new ServiceException("存在重复的商品信息，请确认");
            } else {
                validDuplication.add(validKey);
            }
        });
        //校验数量类型是否存在异常
        prepackagedItems.stream().forEach(info -> {
            if (info.getTotalQuantity() != null) {
                try{
                    info.getTotalQuantity().multiply(BigDecimal.ONE);
                    info.getStockOutQuantity().multiply(BigDecimal.ONE);
                } catch (NumberFormatException e) {
                    throw new ServiceException("总数量或者缺货数量数据类型不正确，请确认！！！");
                }
            }
        });
    }


    /**
     * 根据code更新实体
     *
     * @param entity
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public int updateByCode(Prepackaged entity) {
        entity.setModifiedBy(UserUtil.getCode());
        entity.setModifiedName(partyCache.translate(UserUtil.getCode()));
        return baseMapper.update(entity, new QueryWrapper<Prepackaged>().lambda().eq(Prepackaged::getCode, entity.getCode()).eq(Prepackaged::getGroupCode,UserUtil.getBranchCode()));
    }

    /**
     * 通过code删除预打包主单信息
     * 同时删除预打包明细信息
     * 同时删除预打包分配信息
     * @param code 预打包标识
     */
    @Override
    public void remove(String code) {
        int count = super.count(new QueryWrapper<Prepackaged>().lambda().eq(Prepackaged::getCode, code).eq(Prepackaged::getGroupCode,UserUtil.getBranchCode()));
        if (count == 0) {
            log.error("删除预打包【" + code + "】详情失败，找不到预打包信息");
            throw new ServiceException("删除预打包【" + code + "】详情失败，找不到预打包信息");
        }
        //删除预打包主单信息
        this.remove(new LambdaQueryWrapper<Prepackaged>().eq(Prepackaged::getCode, code).eq(Prepackaged::getGroupCode,UserUtil.getBranchCode()));
        //删除预打包明细信息
        prepackagedItemService.remove(new LambdaQueryWrapper<PrepackagedItem>().eq(PrepackagedItem::getPrepackagedCode, code).eq(PrepackagedItem::getGroupCode,UserUtil.getBranchCode()));
        //删除预打包分配信息
        prepackagedDistributionService.remove(new LambdaQueryWrapper<PrepackagedDistribution>().eq(PrepackagedDistribution::getPrepackagedCode, code).eq(PrepackagedDistribution::getGroupCode,UserUtil.getBranchCode()));
    }

    /**
     * 查询合计,包含总数
     *
     * @param prepackagedDTO
     * @return
     */
    @Override
    public PrepackagedVO queryListSum(PrepackagedDTO prepackagedDTO){
        return this.baseMapper.queryListSum(prepackagedDTO);
    }

    /**
     * 查询列表
     * @param prepackagedDTO
     * @return
     */
    @Override
    public List<PrepackagedVO> queryList(PrepackagedDTO prepackagedDTO){
        return this.baseMapper.queryList(prepackagedDTO);
    }

    /**
     * 获取预打包单详情
     * @param code
     * @return
     */
    @Override
    public PrepackagedVO getVOByCode(String code){
        Prepackaged prepackaged = this.getOne(new QueryWrapper<Prepackaged>().lambda().eq(Prepackaged::getCode, code).eq(Prepackaged::getGroupCode,UserUtil.getBranchCode()));
        PrepackagedVO prepackagedVO = new PrepackagedVO();
        BeanUtil.copyProperties(prepackaged,prepackagedVO);
        List<PrepackagedItemWithDistributionVO> prepackagedItemWithDistributionVOS;
        // 获取带分配信息的明细信息
        prepackagedItemWithDistributionVOS = prepackagedDistributionService.queryWithItemList(prepackaged.getCode());

        prepackagedVO.setPrepackagedItemWithDistributionVOList(prepackagedItemWithDistributionVOS);

        return prepackagedVO;
    }

    /**
     * 分配确认
     *
     * @param code
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Result confirm(String code) {
        // 判断单据状态
        Prepackaged dbEntity = this.getOne(new QueryWrapper<Prepackaged>().lambda().eq(Prepackaged::getCode, code).eq(Prepackaged::getGroupCode,UserUtil.getBranchCode()));
        if (dbEntity == null) {
            throw new ServiceException("预打包单信息不存在");
        }
        if (!StrUtil.containsAny(dbEntity.getStatus(), PrepackagedStatusEnum.PART_DISTRIBUTION.getValue(), PrepackagedStatusEnum.UN_DISTRIBUTION.getValue())) {
            throw new ServiceException("当前预打包单状态无法进行分配完成操作");
        }
        // 获取未生成拣货单的分配信息
        List<PrepackagedItemWithDistributionVO> itemWithDistributions = prepackagedDistributionService.queryWithItemList(code);

        // 判断有未生成拣货单分配信息或单据是部分分配状态， 或判断分配数量
        if (dbEntity.getDistributionQuantity().compareTo(BigDecimal.ZERO) <= 0) {
            throw new ServiceException("没有分配信息，无法进行分配完成操作");
        }
        // 根据分配信息生成拣货单
        if (IterUtil.isNotEmpty(itemWithDistributions)) {
            savePickingAndUpdateDeliveryInfo(dbEntity, itemWithDistributions);
        }


        // 更新预打包单状态为待拣货
        dbEntity.setStatus(PrepackagedStatusEnum.UN_PICKING.getValue());
        this.updateByCode(dbEntity);


        return Result.ok();
    }

    /**
     * 生成拣货单，更新预打包单明细、分配单信息
     * @param dbEntity
     * @param itemWithDistributions
     */
    @Transactional(rollbackFor = {Exception.class})
    public void savePickingAndUpdateDeliveryInfo(Prepackaged dbEntity, List<PrepackagedItemWithDistributionVO> itemWithDistributions) {
        // 生成一个基于数据库实际预打包单的临时数据，调整部分数据，用于生成拣货单
        Prepackaged tempDbEntity = new Prepackaged();
        BeanUtil.copyProperties(dbEntity, tempDbEntity);
        // 修改DistributionNum
        BigDecimal distributionNum = itemWithDistributions.stream().map(PrepackagedItemWithDistributionVO::getDistributionQuantity)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        tempDbEntity.setDistributionQuantity(distributionNum);

        // 根据分配信息生成上架单、上架单明细, 并保存
        Picking picking = generatePicking(tempDbEntity);
        // 明细中商品种类
        Set<String> goodsSortMap = new HashSet<>();
        itemWithDistributions.stream().forEach(info ->{
            goodsSortMap.add(info.getGoodCode());
        });
        picking.setGoodsSortQuantity(new BigDecimal(goodsSortMap.size()));

        //拣货包裹数
        BigDecimal packageQuantity = tempDbEntity.getDistributionQuantity().divide(new BigDecimal(tempDbEntity.getPackageQuantity()), 0, BigDecimal.ROUND_HALF_UP);
        picking.setPackageQuantity(packageQuantity.intValue());
        picking.setGroupCode(dbEntity.getGroupCode());
        pickingService.save(picking);
        List<PickingItem> pickingItems = generatePickingItems(picking, itemWithDistributions);
        pickingItems.stream().forEach(e ->{
            e.setGroupCode(UserUtil.getBranchCode());
        });
        pickingItemService.saveBatch(pickingItems);

        // 更新预打包明细单状态、分配单状态、分配单拣货单号
        List<String> itemCodes = new ArrayList<>();
        List<String> distributionCodes = new ArrayList<>();
        itemWithDistributions.forEach(e->{
            itemCodes.add(e.getPrepackagedItemCode());
            distributionCodes.add(e.getCode());
        });

        prepackagedItemService.update(new UpdateWrapper<PrepackagedItem>().lambda().in(PrepackagedItem::getCode, itemCodes)
                .eq(PrepackagedItem::getGroupCode,UserUtil.getBranchCode())
                .set(PrepackagedItem::getStatus, PrepackagedStatusEnum.UN_PICKING.getValue()));

        prepackagedDistributionService.update(new UpdateWrapper<PrepackagedDistribution>().lambda()
                .in(PrepackagedDistribution::getCode, distributionCodes)
                .eq(PrepackagedDistribution::getGroupCode,UserUtil.getBranchCode())
                .set(PrepackagedDistribution::getDistributionStatus, PrepackagedStatusEnum.UN_PICKING.getValue())
                .set(PrepackagedDistribution::getPickingCode, picking.getCode()));

    }

    /**
     * 根据预打包单生成拣货单
     * @param prepackaged
     * @return
     */
    public Picking generatePicking(Prepackaged prepackaged){
        Picking picking = new Picking();
        BeanUtil.copyProperties(prepackaged, picking);
        picking.setType(DeliveryTypeEnum.PREPACKAGED.getValue());
        picking.setCode(OrderIdGeneratorUtil.generatorOrderId(OrderIdGeneratorUtil.OrderTypeEnum.PK, prepackaged.getShipmentCode(),UserUtil.getBranchCode()));
        picking.setStockoutQuantity(BigDecimal.ZERO);
        picking.setNotPickingQuantity(prepackaged.getDistributionQuantity());
        picking.setPrepackagedCode(prepackaged.getCode());
        picking.setPlanQuantity(prepackaged.getDistributionQuantity());
        picking.setStatus(PickingStatusEnum.NOT_PICK.getValue());
        picking.setSourceCode(prepackaged.getCode());
        picking.setPrepackagedGenerate(NUM_ONE);
        return picking;
    }

    /**
     * 根据拣货单、分配明细、出货分配信息生成拣货明细
     * @param picking
     * @param prepackagedItemWithDistributionVO
     * @return
     */
    public List<PickingItem> generatePickingItems(Picking picking, List<PrepackagedItemWithDistributionVO> prepackagedItemWithDistributionVO){
        List<PickingItem> pickingItems = new ArrayList<>();
        List<PickingRegister> pickingRegisters = new ArrayList<>();
        PickingItem pickingItem;
        PickingRegister pickingRegister;

        for(PrepackagedItemWithDistributionVO temp: prepackagedItemWithDistributionVO) {
            // 分配数量大于0 才生成拣货明细
            if (temp.getDistributionQuantity() != null && temp.getDistributionQuantity().compareTo(BigDecimal.ZERO) > 0) {
                pickingItem = new PickingItem();
                BeanUtil.copyProperties(temp, pickingItem);
                pickingItem.setCode(IdUtil.simpleUUID());
                pickingItem.setCreatedBy(UserUtil.getCode());
                pickingItem.setPickingCode(picking.getCode());
                pickingItem.setPrepackagedDistributionCode(temp.getCode());
                pickingItem.setStockoutQuantity(BigDecimal.ZERO);
                // 待拣货状态
                pickingItem.setStatus(PickingStatusEnum.NOT_PICK.getValue());

                pickingItems.add(pickingItem);

                // 生成一条未收货的拣货确认单
                pickingRegister = new PickingRegister();
                BeanUtil.copyProperties(temp, pickingRegister);
                pickingRegister.setCode(IdUtil.simpleUUID());
                pickingRegister.setPickingItemCode(pickingItem.getCode());
                pickingRegister.setCreatedBy(UserUtil.getCode());
                pickingRegister.setPickingCode(picking.getCode());
                pickingRegister.setDeliveryDistributionCode(temp.getCode());
                // 未收货数量初始值为分配数量
                pickingRegister.setNotPickingQuantity(pickingItem.getDistributionQuantity());
                // 拣货数量初始值也是分配数量，冗余字段，用于与其他确认单计算时统一字段
                pickingRegister.setPickingQuantity(pickingItem.getDistributionQuantity());
                // 确认单状态为未拣货状态
                pickingRegister.setStatus(PickingRegisterStatusEnum.NOT_PICK.getValue());
                pickingRegisters.add(pickingRegister);
            }

        }

        pickingRegisters.stream().forEach(e ->{
            e.setGroupCode(UserUtil.getBranchCode());
        });
        // 保存初始默认的拣货确认单
        pickingRegisterService.saveBatch(pickingRegisters);

        return  pickingItems;
    }
    /**
     * 获取 预打包分配库存信息 列表
     *
     * @param itemCode
     * @return
     */
    @Override
    public Result queryInventory(String itemCode){
        PrepackagedItem item = prepackagedItemService.getOne(new LambdaQueryWrapper<PrepackagedItem>().eq(PrepackagedItem::getCode, itemCode).eq(PrepackagedItem::getGroupCode,UserUtil.getBranchCode()));
        if(null == item){
            return Result.error("不存在该明细信息，请确认");
        }

        Prepackaged prepackaged = this.getOne(new QueryWrapper<Prepackaged>().lambda().eq(Prepackaged::getCode, item.getPrepackagedCode()).eq(Prepackaged::getGroupCode,UserUtil.getBranchCode()));

        PrepackagedItemVO prepackagedItemVO = new PrepackagedItemVO();
        BeanUtil.copyProperties(item, prepackagedItemVO);

        List<InventoryWithDistributionVO> inventoryWithDistributionVOS = inventoryService.queryCanDistributeInventory(prepackaged.getWarehouseCode(),prepackaged.getShipmentCode(), item.getGoodsCode());
        prepackagedItemVO.setInventoryWithDistributionVOList(inventoryWithDistributionVOS);

        return Result.ok().data(prepackagedItemVO);
    }

    /**
     * 取消分配
     *
     * @param entity
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Result cancelDistribution(PrepackagedVO entity) {
        // 判断状态是否可取消分配
        Prepackaged prepackaged = getOne(new QueryWrapper<Prepackaged>().lambda().eq(Prepackaged::getCode, entity.getCode()).eq(Prepackaged::getGroupCode,UserUtil.getBranchCode()));
        // 取消前的处理，如校验单据状态、清除生成的上架单信息
        List<PrepackagedItemWithDistributionVO> prepackagedItemWithDistributionVOS = preCancelDistribution(entity, prepackaged);
        // 如果处理结果为null，则说明没有可用的取消信息，可以直接返回
        if (IterUtil.isEmpty(prepackagedItemWithDistributionVOS)) {
            return Result.ok();
        }

        Map<String, BigDecimal> itemUpdateInfo;
        itemUpdateInfo = new HashMap<>();
        BigDecimal prepackagedUpdateQuantity = BigDecimal.ZERO;
        PrepackagedDistributionDTO prepackagedDistributionDTO;
        Inventory tempInventory;
        int compareQuantityFlag = 0;
        BigDecimal canCancelQuantity;
        // 遍历要取消的分配信息
        for (PrepackagedItemWithDistributionVO itemDistribution : prepackagedItemWithDistributionVOS) {
            canCancelQuantity = itemDistribution.getDistributionQuantity();
            canCancelQuantity = canCancelQuantity == null ? BigDecimal.ZERO : canCancelQuantity;
            compareQuantityFlag = canCancelQuantity.compareTo(itemDistribution.getCancelQuantity());
            // 若完全取消，则删除分配表，若不完全取消，则更新分配表
            if (compareQuantityFlag == 0) {
                prepackagedDistributionService.remove(new QueryWrapper<PrepackagedDistribution>().lambda().eq(PrepackagedDistribution::getCode, itemDistribution.getCode()).eq(PrepackagedDistribution::getGroupCode,UserUtil.getBranchCode()));
                // 如果全取消掉，则会调整预打包单状态，改为“待分配”
                updatePrepackagedStatusIfEmptyDistribution(itemDistribution.getPrepackagedCode());
            } else if (compareQuantityFlag > 0) {
                // 不完全取消，更新分配表，如果调整后有其他相同的数据，则需要合并
                PrepackagedDistribution sameDistribution = prepackagedDistributionService.hasSameDistribution(itemDistribution.getPrepackagedItemCode(), itemDistribution.getCode());
                if (sameDistribution != null) {
                    // 计算出剩余的分配数量
                    BigDecimal remainQuantity = canCancelQuantity.subtract(itemDistribution.getCancelQuantity());
                    // 如果有相同的分配单，则删除当前分配单，改为调整相同的那个分配单数量
                    prepackagedDistributionDTO = new PrepackagedDistributionDTO();
                    prepackagedDistributionDTO.setCode(itemDistribution.getCode());
                    prepackagedDistributionDTO.setDistributionQuantity(remainQuantity);
                    prepackagedDistributionDTO.setCode(sameDistribution.getCode());
                    prepackagedDistributionService.remove(new QueryWrapper<PrepackagedDistribution>().lambda().eq(PrepackagedDistribution::getCode, itemDistribution.getCode()).eq(PrepackagedDistribution::getGroupCode,UserUtil.getBranchCode()));
                    prepackagedDistributionService.adjustDistributionQuantity(prepackagedDistributionDTO);
                } else {
                    // 如果没有相同分配单，则调整当前分配单，减掉取消数量、清除拣货单code
                    prepackagedDistributionService.update(new UpdateWrapper<PrepackagedDistribution>().lambda()
                            .eq(PrepackagedDistribution::getCode, itemDistribution.getCode())
                            .eq(PrepackagedDistribution::getGroupCode,UserUtil.getBranchCode())
                            .set(PrepackagedDistribution::getPickingCode, StrUtil.EMPTY)
                            .setSql(" distribution_quantity = distribution_quantity - " + itemDistribution.getCancelQuantity()));
                }
            } else {
                // 取消数量超过了可退数量，需要回滚
                throw new ServiceException("取消数量不能大于分配数量");
            }
            // 释放部分临时库存
            tempInventory = inventoryService.getOne(new QueryWrapper<Inventory>().lambda().eq(Inventory::getCode, itemDistribution.getInventoryCode()).eq(Inventory::getGroupCode,UserUtil.getBranchCode()));
            inventoryService.returnInventory(tempInventory, itemDistribution.getCancelQuantity());

            // 判断是否要将临时库存作业状态转为正常
            inventoryService.releaseEmptyTempInvJobStatus(tempInventory.getCode());

            // 记录库存变动日志
            Inventory inv = inventoryService.getOne(new QueryWrapper<Inventory>().lambda().eq(Inventory::getCode, tempInventory.getOldInventoryCode()).eq(Inventory::getGroupCode,UserUtil.getBranchCode()));
            inventoryRecordService.insertRecord(tempInventory, prepackaged.getCode(), itemDistribution.getCancelQuantity().negate(), InventoryRecordTypeEnum.PICK.getValue());
            inventoryRecordService.insertRecord(inv, prepackaged.getCode(), itemDistribution.getCancelQuantity(), InventoryRecordTypeEnum.PICK.getValue());

            // 根据item的code分组累计取消量
            itemUpdateInfo.put(itemDistribution.getPrepackagedItemCode(),
                    itemUpdateInfo.getOrDefault(itemDistribution.getPrepackagedItemCode(), BigDecimal.ZERO).add(itemDistribution.getCancelQuantity()));
            //  累计总取消量
            prepackagedUpdateQuantity = prepackagedUpdateQuantity.add(itemDistribution.getCancelQuantity());
        }

        // 更新预打包单、明细的状态、分配数量
        updateCancelInfo(prepackaged, prepackagedUpdateQuantity, itemUpdateInfo);

        return Result.ok();
    }

    /**
     * 取消前校验、处理部分数据
     *
     * @param entity
     * @param prepackaged
     * @return
     */
    @Transactional(rollbackFor = {Exception.class})
    public List<PrepackagedItemWithDistributionVO> preCancelDistribution(PrepackagedVO entity, Prepackaged prepackaged) {
        if (ObjectUtil.isNull(prepackaged)) {
            throw new ServiceException("单据不存在，请确认");
        }

        if (!(StrUtil.equalsAny(prepackaged.getStatus(), PrepackagedStatusEnum.UN_PICKING.getValue(), PrepackagedStatusEnum.PART_DISTRIBUTION.getValue()))) {
            throw new ServiceException("预打包单当前状态不能取消");
        }

        // 根据所有上架单状态判断是否可以取消分配，没有上架单的则可以取消分配
        List<PrepackagedItemWithDistributionVO> prepackagedItemWithDistributionVOS = entity.getPrepackagedItemWithDistributionVOList();
        // 过滤出要取消的分配信息
        prepackagedItemWithDistributionVOS = prepackagedItemWithDistributionVOS.stream().filter(e -> e.getCancelQuantity() != null
                && e.getCancelQuantity().compareTo(BigDecimal.ZERO) > 0).collect(Collectors.toList());

        // 获取所有要取消的分配单号
        List<String> distributionCodes = prepackagedItemWithDistributionVOS.stream().map(e -> e.getCode()).collect(Collectors.toList());
        // 如果没有取消的信息，则直接返回null
        if (prepackagedItemWithDistributionVOS.isEmpty()) {
            return prepackagedItemWithDistributionVOS;
        }
        // 从数据库中获取所有要取消的分配信息
        List<PrepackagedDistribution> distributions = prepackagedDistributionService.list(new QueryWrapper<PrepackagedDistribution>().lambda()
                .in(PrepackagedDistribution::getCode, distributionCodes)
                .eq(PrepackagedDistribution::getGroupCode,UserUtil.getBranchCode()));
        // 分组获取拣货单号
        List<String> pickingCodes = distributions.stream().map(e -> e.getPickingCode()).collect(Collectors.toList());
        // 查询这些拣货单中上架数量超过0的数据
        List<Picking> cannotCancelPickings = pickingService.list(new QueryWrapper<Picking>().lambda().in(Picking::getCode, pickingCodes)
                .eq(Picking::getGroupCode,UserUtil.getBranchCode())
                .gt(Picking::getPickingQuantity, BigDecimal.ZERO));
        String cannotCancelPickingCodes = cannotCancelPickings.stream().map(e -> e.getCode()).collect(Collectors.joining(","));
        // 如果存在上架数量超过0的，则不允许取消
        if (IterUtil.isNotEmpty(cannotCancelPickings)) {
            throw new ServiceException("上架单【" + cannotCancelPickingCodes + "】已产生上架信息，无法取消");
        }
        // 如果上架单已下发生成任务则也不可以取消
        pickingCodes.forEach(e -> {
            boolean taskFlag = wmsTaskService.hasUnFinishTaskForSourceCode(e);
            if (taskFlag) {
                throw new ServiceException("拣货单【" + e + "】已生成任务，且任务未完成，无法取消分配");
            }
        });

        // 删除所有要取消的上架单、上架单明细、上架确认单
        pickingRegisterService.remove(new QueryWrapper<PickingRegister>().lambda().in(PickingRegister::getPickingCode, pickingCodes).eq(PickingRegister::getGroupCode,UserUtil.getBranchCode()));
        pickingItemService.remove(new QueryWrapper<PickingItem>().lambda().in(PickingItem::getPickingCode, pickingCodes).eq(PickingItem::getGroupCode,UserUtil.getBranchCode()));
        pickingService.remove(new QueryWrapper<Picking>().lambda().in(Picking::getCode, pickingCodes).eq(Picking::getGroupCode,UserUtil.getBranchCode()));
        // 清除掉要取消的分配单上的上架单信息
        prepackagedDistributionService.update(new UpdateWrapper<PrepackagedDistribution>().lambda()
                .in(PrepackagedDistribution::getPickingCode, pickingCodes)
                .eq(PrepackagedDistribution::getGroupCode,UserUtil.getBranchCode())
                .set(PrepackagedDistribution::getPickingCode,StrUtil.EMPTY));

        return prepackagedItemWithDistributionVOS;
    }

    /**
     * 预打包单分配量为0则更新状态为“待分配”
     * @param prepackagedCode
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void updatePrepackagedStatusIfEmptyDistribution(String prepackagedCode){
        // 判断预打包单分配数量是否已小于等于0, 分配数量等于0则将分配单状态设为“待分配”
        Prepackaged prepackaged = getOne(new QueryWrapper<Prepackaged>().lambda().eq(Prepackaged::getCode, prepackagedCode)
                .eq(Prepackaged::getGroupCode,UserUtil.getBranchCode())
                .le(Prepackaged::getDistributionQuantity, BigDecimal.ZERO));
        if (prepackaged != null) {
            update(new UpdateWrapper<Prepackaged>().lambda().eq(Prepackaged::getCode, prepackagedCode)
                    .eq(Prepackaged::getGroupCode,UserUtil.getBranchCode())
                    .set(Prepackaged::getStatus, PrepackagedStatusEnum.UN_DISTRIBUTION.getValue()));
        }
    }

    /**
     * 取消分配后，需要更新发货单、发货明细的状态、分配数量
     *
     * @param prepackaged
     * @param prepackagedUpdateQuantity
     * @param itemUpdateInfo
     */
    @Transactional(rollbackFor = {Exception.class})
    public void updateCancelInfo(Prepackaged prepackaged, BigDecimal prepackagedUpdateQuantity, Map<String, BigDecimal> itemUpdateInfo) {
        // 更新主表总分配量、状态（部分分配或待分配)
        LambdaUpdateWrapper<Prepackaged> updateWrapper = new UpdateWrapper<Prepackaged>().lambda();
        updateWrapper.eq(Prepackaged::getCode, prepackaged.getCode())
        .eq(Prepackaged::getGroupCode,UserUtil.getBranchCode());
        updateWrapper.setSql(" distribution_quantity = distribution_quantity + " + prepackagedUpdateQuantity.negate());
        updateWrapper.setSql(" distribution_pack_quantity = distribution_pack_quantity + " + prepackagedUpdateQuantity.negate().divide(prepackaged.getTotalCombinationQuantity(),0,BigDecimal.ROUND_HALF_UP).intValue());
        prepackaged = getOne(new QueryWrapper<Prepackaged>().lambda().eq(Prepackaged::getCode, prepackaged.getCode()));
        if (prepackaged.getDistributionQuantity().compareTo(BigDecimal.ZERO) > 0 && prepackaged.getDistributionQuantity().compareTo(prepackagedUpdateQuantity) > 0) {
            updateWrapper.set(Prepackaged::getStatus, PrepackagedStatusEnum.PART_DISTRIBUTION.getValue());
        } else {
            updateWrapper.set(Prepackaged::getStatus, PrepackagedStatusEnum.UN_DISTRIBUTION.getValue());
        }
        this.update(updateWrapper);

        // 更新子表状态、分配数量
        PrepackagedItemDTO prepackagedItemDTO;
        for (String itemCode : itemUpdateInfo.keySet()) {
            prepackagedItemDTO = new PrepackagedItemDTO();
            prepackagedItemDTO.setCode(itemCode);
            prepackagedItemDTO.setDistributionQuantity(itemUpdateInfo.get(itemCode).negate());
            prepackagedItemService.adjustDistributionQuantity(prepackagedItemDTO);
        }
    }
    /**
     * 预打包生成拣货单
     * @param code
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Result genPicking(String code){
        // 判断单据状态
        Prepackaged dbEntity = this.getOne(new QueryWrapper<Prepackaged>().lambda().eq(Prepackaged::getCode, code).eq(Prepackaged::getGroupCode,UserUtil.getBranchCode()));
        if (dbEntity == null) {
            throw new ServiceException("预打包单信息不存在");
        }
        if (!StrUtil.containsAny(dbEntity.getStatus(), PrepackagedStatusEnum.PART_DISTRIBUTION.getValue(), PrepackagedStatusEnum.UN_DISTRIBUTION.getValue())) {
            throw new ServiceException("当前预打包单状态无法生成拣货单");
        }
        // 获取未生成拣货单的分配信息
        List<PrepackagedItemWithDistributionVO> itemWithDistributions = prepackagedDistributionService.queryWithItemList(code);
        //校验生成拣货单条件
        boolean canGenPicking = validateGenPicking(dbEntity,itemWithDistributions);

        if (canGenPicking) {
            // 根据分配信息生成拣货单
            if (IterUtil.isNotEmpty(itemWithDistributions)) {
                savePickingAndUpdateDeliveryInfo(dbEntity, itemWithDistributions);
            }
            // 更新预打包单状态为待拣货
            dbEntity.setStatus(PrepackagedStatusEnum.UN_PICKING.getValue());
            this.updateByCode(dbEntity);
        }

        return Result.ok();

    }

    private boolean validateGenPicking(Prepackaged dbEntity,List<PrepackagedItemWithDistributionVO> itemWithDistributions) {
        // 1.各商品分配数量/单件数量等于零,即预打包主单分配数量为0，提示：商品分配数量为0 ，不能生成拣货单；
        if (dbEntity.getDistributionQuantity().compareTo(BigDecimal.ZERO) <= 0) {
            throw new ServiceException("商品分配数量为0 ，不能生成拣货单");
        }
        Set<BigDecimal> totalPackNum = new HashSet<>();
        //2、各商品分配数量/单件数量大于零且不相等，提示：分配数量与包裹数量不一致，不能生成拣货单；
        for (PrepackagedItemWithDistributionVO distributionVO : itemWithDistributions) {
            if (distributionVO.getDistributionQuantity() == null) {
                distributionVO.setDistributionQuantity(BigDecimal.ZERO);
            }
            BigDecimal packQuantity = distributionVO.getDistributionQuantity().divide(distributionVO.getCombinationQuantity(),6, BigDecimal.ROUND_HALF_UP);
            if (packQuantity.compareTo(BigDecimal.ZERO) == 0) {
                throw new ServiceException("存在商品分配数量/单件数量等于零的明细，不能生成拣货单");
            }
            if (totalPackNum.size() > NumConst.NUM_ONE) {
                throw new ServiceException("分配数量与包裹数量不一致，不能生成拣货单");
            } else {
                if (!totalPackNum.contains(packQuantity)) {
                    totalPackNum.add(packQuantity);
                }
            }
        }
        //3.各商品分配数量/单件数量大于零且相等且打包数量为整数，更新预打包单状态为待拣货，生成拣货单判断
        if (totalPackNum.size() == NumConst.NUM_ONE ) {
            BigDecimal packQuantity = totalPackNum.iterator().next();
            BigDecimal packNum = packQuantity.setScale(0, BigDecimal.ROUND_HALF_DOWN);
            if (packQuantity.compareTo(packNum) == 0) {
                return true;
            } else {
                throw new ServiceException("打包数量必须为整数，请重新分配库存");
            }
        } else {
            throw new ServiceException("包裹数量不一致，不能生成拣货单");
        }
    }

    /**
     * 更新打包数量及状态
     * @param prepackagedDTO
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public int updatePackQuantityAndStatus(PrepackagedDTO prepackagedDTO){
        return this.baseMapper.updatePackQuantityAndStatus(prepackagedDTO);
    }

    /**
     * 预打包任务下发
     * @param prepackagedDTO
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Result issuePrepackaged(PrepackagedDTO prepackagedDTO){
        prepackagedDTO.setGroupCode(UserUtil.getBranchCode());
        Prepackaged prepackaged = this.getOne(new LambdaQueryWrapper<Prepackaged>().eq(Prepackaged::getCode, prepackagedDTO.getCode()).eq(Prepackaged::getGroupCode,UserUtil.getBranchCode()));
        if (null == prepackaged) {
            throw new ServiceException("预打包单信息不存在，请确认");
        }
        //1.检验下发预打包数量及计费方式
        validatePrepackagedData(prepackagedDTO,prepackaged);
        //2.生成下发任务及明细
        generatePackTask(prepackagedDTO,prepackaged);
        //3.更新预打包信息
        updatePrepackagedInfo(prepackagedDTO,prepackaged);
        return Result.ok();
    }

    private void validatePrepackagedData(PrepackagedDTO prepackagedDTO,Prepackaged prepackaged) {
        //下发包裹数量校验
        validateIssuePack(prepackagedDTO,prepackaged);
        //计费方式校验并判断是否生成打包计费方式信息
        PackBillingMethodDTO packBillingMethodDTO = new PackBillingMethodDTO();
        BeanUtil.copyProperties(prepackagedDTO,packBillingMethodDTO);
        packBillingMethodDTO.setPartyCode(prepackagedDTO.getPartyCode());
        packBillingMethodDTO.setStaffName(prepackagedDTO.getStaffName());
        packTaskService.validatePackBillingMethod(packBillingMethodDTO);
    }

    private void validateIssuePack(PrepackagedDTO prepackagedDTO,Prepackaged prepackaged) {
        if (prepackaged.getIssue() == 1) {
            throw new ServiceException("该预打包已下发完毕，无法再次下发");
        }
        Integer planPackQuantity = prepackagedDTO.getPickingQuantity();
        //本次下发包裹数量
        Integer issueQuantity = prepackagedDTO.getIssueQuantity();
        //总的下发数量
        Integer totalIssueQuantity = issueQuantity + prepackaged.getIssueQuantity();
        if (totalIssueQuantity > planPackQuantity) {
            throw new ServiceException("下发总数量大于待下发数量，请确认");
        }
    }

    private void generatePackTask(PrepackagedDTO prepackagedDTO,Prepackaged prepackaged) {
        String createdBy = UserUtil.getCode();
        String createdName = partyCache.translate(createdBy);
        LocalDateTime nowTime = LocalDateTime.now();
        BigDecimal goodsQuantity = new BigDecimal(prepackagedDTO.getIssueQuantity()).multiply(prepackaged.getTotalCombinationQuantity());
        //生成出库任务明细
        PackTaskItem item = generatePackTaskItem(prepackaged);
        PackTask packTask = new PackTask();
        BeanUtil.copyProperties(prepackagedDTO,packTask,false);
        // 使用PACK+日期+流水号
        String codePrefix = OrderIdGeneratorUtil.OrderTypeEnum.PACK + DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN);
        packTask.setCode(codePrefix + OrderIdGeneratorUtil.getAutoIncrementIndex(codePrefix,3));
        packTask.setWarehouseCode(prepackaged.getWarehouseCode());
        packTask.setWarehouseName(prepackaged.getWarehouseName());
        packTask.setShipmentCode(prepackaged.getShipmentCode());
        packTask.setShipmentName(prepackaged.getShipmentName());
        packTask.setGroupCode(UserUtil.getBranchCode());
        packTask.setJobType(JobTypeEnum.PACK.getValue());
        packTask.setOrderType(CommonConst.PREPACKAGED);
        packTask.setOrderQuantity(NumConst.NUM_ONE);
        packTask.setGoodsQuantity(goodsQuantity);
        packTask.setStatus(PackTaskStatusEnum.WAITING.getValue());
        packTask.setPackageQuantity(prepackagedDTO.getIssueQuantity());
        packTask.setPrepackagedCode(prepackaged.getCode());
        packTask.setCreatedBy(createdBy);
        packTask.setCreatedName(createdName);
        packTask.setGmtCreated(nowTime);
        packTask.setModifiedBy(createdBy);
        packTask.setModifiedName(createdName);
        packTask.setGmtModified(nowTime);
        item.setPackTaskCode(packTask.getCode());
        packTaskService.save(packTask);
        packTaskItemService.save(item);
    }

    private PackTaskItem generatePackTaskItem(Prepackaged prepackaged) {
        PackTaskItem item = new PackTaskItem();
        item.setCode(IdUtil.simpleUUID());
        item.setSourceCode(prepackaged.getCode());
        item.setGmtCreated(LocalDateTime.now());
        item.setCreatedBy(UserUtil.getCode());
        item.setCreatedName(partyCache.translate(UserUtil.getCode()));
        item.setGroupCode(UserUtil.getBranchCode());
        return item;
    }

    private void updatePrepackagedInfo(PrepackagedDTO prepackagedDTO,Prepackaged prepackaged){
        //更新数据用
        PrepackagedDTO dto = new PrepackagedDTO();
        Integer totalIssueQuantity = prepackaged.getIssueQuantity() + prepackagedDTO.getIssueQuantity();
        if (totalIssueQuantity.equals(prepackaged.getPickingQuantity())) {
            dto.setIssue(NumConst.NUM_ONE);
        }
        dto.setIssueQuantity(totalIssueQuantity);
        dto.setCode(prepackaged.getCode());
        updatePackQuantityAndStatus(dto);
    }

    @Override
    public PrepackagedVO getPrepackagedVOByEntity(Prepackaged prepackaged) {
        PrepackagedVO prepackagedVO = new PrepackagedVO();
        BeanUtil.copyProperties(prepackaged, prepackagedVO);
        return prepackagedVO;
    }

    /**
     * 更新已打包数量及状态
     * @param prepackagedDTO
     * @return
     */
    @Override
    public int updatePackedQuantity(PrepackagedDTO prepackagedDTO){
        return this.baseMapper.updatePackedQuantity(prepackagedDTO);
    }


}
